//
//  LocationManager.swift
//  Scaner
//
//  Created by liyahao on 2025/4/15.
//

import Foundation
import CoreLocation
import Contacts

class LocationManager: NSObject, CLLocationManagerDelegate {

    // MARK: - Properties
    static let shared = LocationManager()
    private let locationManager = CLLocationManager()
    private let geocoder = CLGeocoder() // Add a CLGeocoder instance

    var authorizationStatus: CLAuthorizationStatus {
        return locationManager.authorizationStatus
    }
    
    // 当前位置
    public var currentLocation: CLLocation?
    public var currentPlacemark: CLPlacemark?

    // To prevent excessive geocoding requests if locations update rapidly
    private var isPerformingReverseGeocode = false

    // --- Callbacks ---
    var didUpdateLocation: ((CLLocation) -> Void)?
    var didChangeAuthorization: ((CLAuthorizationStatus) -> Void)?
    var didFailWithError: ((Error) -> Void)?
    // New callback for placemark or formatted address string
    var didUpdatePlacemark: ((CLPlacemark) -> Void)? // Option 1: Return the whole placemark
    var didUpdatePlaceName: ((String) -> Void)?    // Option 2: Return a formatted string


    // MARK: - Initialization
    private override init() {
        super.init()
        setupLocationManager()
    }

    // MARK: - Setup
    private func setupLocationManager() {
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        locationManager.distanceFilter = 300 // Consider using a distance filter
    }

    // MARK: - Public Methods
    // ... (requestWhenInUseAuthorization, requestAlwaysAuthorization, startUpdatingLocation, stopUpdatingLocation remain the same) ...
    func requestWhenInUseAuthorization() {
        locationManager.requestWhenInUseAuthorization()
    }

    func requestAlwaysAuthorization() {
        locationManager.requestAlwaysAuthorization()
    }

    func startUpdatingLocation() {
        let status = locationManager.authorizationStatus
        if status == .authorizedWhenInUse || status == .authorizedAlways {
            print("Location Manager: Starting location updates.")
            locationManager.startUpdatingLocation()
        } else if status == .notDetermined {
            print("Location Manager: Permission not determined. Requesting WhenInUse authorization.")
            requestWhenInUseAuthorization()
        } else {
            print("Location Manager: Cannot start updates. Permission denied or restricted.")
             didChangeAuthorization?(status)
        }
    }

    func stopUpdatingLocation() {
        print("Location Manager: Stopping location updates.")
        locationManager.stopUpdatingLocation()
        // Also cancel any ongoing geocoding task if needed
        if geocoder.isGeocoding {
            geocoder.cancelGeocode()
            isPerformingReverseGeocode = false // Reset flag if cancelled
        }
    }


    // MARK: - CLLocationManagerDelegate Methods

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        self.currentLocation = location
        print("Location Manager: Did update location - \(location.coordinate.latitude), \(location.coordinate.longitude)")

        // --- Trigger Location Update Callback Immediately ---
        didUpdateLocation?(location)

        // --- Perform Reverse Geocoding ---
        // Basic check to avoid hammering the geocoder if updates are very frequent
        guard !isPerformingReverseGeocode else {
            // print("Location Manager: Skipping geocode, previous request still in progress or location hasn't changed significantly.")
            return
        }
        performReverseGeocoding(for: location)
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Location Manager: Did fail with error: \(error.localizedDescription)")
        didFailWithError?(error)
         if let clError = error as? CLError, clError.code == .denied {
             print("Location Manager: Location access denied by user.")
             stopUpdatingLocation()
         }
    }

    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        // ... (This method remains largely the same, handling authorization changes) ...
        let status = manager.authorizationStatus
        print("Location Manager: Did change authorization status to: \(status.description)") // Using helper extension

        didChangeAuthorization?(status)

        switch status {
        case .authorizedWhenInUse, .authorizedAlways:
             print("Location Manager: Authorization granted.")
             // Consider starting updates if appropriate for your app logic
             // startUpdatingLocation()
        case .denied, .restricted:
            print("Location Manager: Authorization denied or restricted. Stopping updates.")
            stopUpdatingLocation()
            currentLocation = nil
            currentPlacemark = nil // Clear placemark too
        case .notDetermined:
            print("Location Manager: Authorization not determined.")
        @unknown default:
            print("Location Manager: Unknown authorization status.")
            stopUpdatingLocation()
        }
    }

    // MARK: - Reverse Geocoding Helper

    private func performReverseGeocoding(for location: CLLocation) {
        // Prevent multiple simultaneous requests
        guard !geocoder.isGeocoding else {
             print("Location Manager: Geocoder busy.")
             return
        }

        isPerformingReverseGeocode = true // Set flag
        print("Location Manager: Starting reverse geocode for location...")

        geocoder.reverseGeocodeLocation(location) { [weak self] (placemarks, error) in
            guard let self = self else { return }
            self.isPerformingReverseGeocode = false // Reset flag when done or failed

            if let error = error {
                // Ignore certain types of errors like frequent requests if needed
                if let clErr = error as? CLError, clErr.code == .network || clErr.code == .geocodeFoundNoResult {
                     print("Location Manager: Geocode failed (Network or No Result): \(error.localizedDescription)")
                    // Don't necessarily trigger the main error handler for these common issues
                } else if let clErr = error as? CLError, clErr.code == .geocodeCanceled {
                    print("Location Manager: Geocode cancelled.")
                    // Don't trigger error callback if it was intentionally cancelled
                }
                else {
                    print("Location Manager: Reverse geocode failed with error: \(error.localizedDescription)")
                    self.didFailWithError?(error) // Propagate other errors
                }
                self.currentPlacemark = nil // Clear placemark on error
                return
            }

            guard let placemark = placemarks?.first else {
                print("Location Manager: No placemarks found.")
                self.currentPlacemark = nil // Clear placemark if none found
                return
            }

            self.currentPlacemark = placemark // Store the latest placemark
            print("Location Manager: Reverse geocode successful.")

            // --- Trigger Placemark Callbacks ---

            // Option 1: Callback with the full CLPlacemark object
            self.didUpdatePlacemark?(placemark)

            // Option 2: Callback with a formatted string
            if let formattedName = self.formatPlacemark(placemark) {
                print("Location Manager: Formatted place name - \(formattedName)")
                self.didUpdatePlaceName?(formattedName)
            }
        }
    }

    // MARK: - Placemark Formatting Helper

    /// Creates a user-friendly string from a CLPlacemark.
    /// Customize this based on the level of detail you need.
    private func formatPlacemark(_ placemark: CLPlacemark) -> String? {
        // Using CNPostalAddressFormatter for a localized address format (iOS 11+)
        // Requires importing Contacts framework
        let postalAddress = CNMutablePostalAddress()
        postalAddress.street = [placemark.thoroughfare, placemark.subThoroughfare]
            .compactMap { $0 } // Remove nils
            .joined(separator: " ") // Join street number and name
        postalAddress.city = placemark.locality ?? ""
        postalAddress.state = placemark.administrativeArea ?? ""
        postalAddress.postalCode = placemark.postalCode ?? ""
        postalAddress.country = placemark.country ?? ""
        postalAddress.isoCountryCode = placemark.isoCountryCode ?? ""

        let formattedAddress = CNPostalAddressFormatter.string(from: postalAddress, style: .mailingAddress)

        if !formattedAddress.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
             return formattedAddress.replacingOccurrences(of: "\n", with: ", ") // Replace newlines with commas for a single line
        }


        // --- Fallback / Simpler Formatting (if CNPostalAddressFormatter doesn't work or for simpler cases) ---
        var addressParts: [String] = []

        // More specific name if available (like a park or building)
        if let name = placemark.name, !name.contains(where: { $0.isNumber }) { // Avoid using raw coordinates as name
            addressParts.append(name)
        } else {
             // Street address
            let street = [placemark.subThoroughfare, placemark.thoroughfare].compactMap({ $0 }).joined(separator: " ")
            
            if !street.isEmpty {
                addressParts.append(street)
            }
        }

        // City
        if let city = placemark.locality, !city.isEmpty {
            addressParts.append(city)
        }

        // State
        if let state = placemark.administrativeArea, !state.isEmpty {
            addressParts.append(state)
        }

        // Country (optional, maybe too verbose)
         if let country = placemark.country, !country.isEmpty, addressParts.count < 2 { // Add country if few other details
             addressParts.append(country)
         }

        let addressString = addressParts.joined(separator: ", ")
        return addressString.isEmpty ? nil : addressString // Return nil if empty
    }
}

// Keep the CLAuthorizationStatus extension for description
extension CLAuthorizationStatus {
    var description: String {
        switch self {
        case .notDetermined: return "Not Determined"
        case .restricted: return "Restricted"
        case .denied: return "Denied"
        case .authorizedAlways: return "Authorized Always"
        case .authorizedWhenInUse: return "Authorized When In Use"
        @unknown default: return "Unknown"
        }
    }
}
